﻿using CSRedis;

namespace YunQue.Core.Common.Caches
{
    /// <summary>
    /// RedisWrapper
    /// </summary>
    public class RedisWrapper
    {
        private static CSRedisClient _redisClient = null;

        private static readonly object _locker = new object();
        static RedisWrapper()
        {
            if (_redisClient == null)
            {
                lock (_locker)
                {
                    if (_redisClient == null)
                    {
                        //哨兵模式
                        //_redisClient = new CSRedisClient(AppSettingManager.REDIS_MASTER, AppSettingManager.REDIS_SENTINELS.Split(";"));
                        //单机模式
                        _redisClient = new CSRedisClient(AppSettingManager.REDIS_CONNECTION);
                    }
                }
            }
        }
        /// <summary>
        /// Set
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="data"></param>
        /// <param name="expiry"></param>
        public static void Set<T>(string key, T data, TimeSpan? expiry = null)
        {
            _redisClient.Set(key, data, expiry == null ? -1 : expiry.Value.Seconds);
        }
        /// <summary>
        /// 向集合中添加元素
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="data"></param>
        public static void SetAdd<T>(string key, T data)
        {
            _redisClient.SAdd(key, data);
        }
        /// <summary>
        /// set async
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="data"></param>
        /// <param name="expiry"></param>
        /// <returns></returns>
        public static async Task<bool> SetAsync<T>(string key, T data, TimeSpan? expiry = null)
        {
            return await _redisClient.SetAsync(key, data, expiry == null ? -1 : expiry.Value.Seconds).ConfigureAwait(false);
        }
        /// <summary>
        /// Get
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public static T Get<T>(string key)
        {
            return _redisClient.Get<T>(key);
        }
        /// <summary>
        /// 从集合中获取指定下标的元素
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        public static T GetItemFromList<T>(string key, int index)
        {
            return _redisClient.LIndex<T>(key, index);
        }
        /// <summary>
        /// get async
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public static async Task<T> GetAsync<T>(string key)
        {
            return await _redisClient.GetAsync<T>(key).ConfigureAwait(false);
        }

        /// <summary>
        /// 移除
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static bool Remove(string key)
        {
            return _redisClient.Del(key) > 0;
        }
        /// <summary>
        /// 移除
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static async Task<bool> RemoveAsync(string key)
        {
            return await _redisClient.DelAsync(key).ConfigureAwait(false) > 0;
        }
        /// <summary>
        /// 向集合中添加元素
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        public static async Task<long> SAddAsync<T>(string key, T data)
        {
            return await _redisClient.SAddAsync(key, data).ConfigureAwait(false);
        }
        /// <summary>
        /// 向集合中添加元素
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="datas"></param>
        /// <returns></returns>
        public static async Task<long> SAddListAsync<T>(string key, List<T> datas)
        {
            return await _redisClient.SAddAsync(key, datas.ToArray()).ConfigureAwait(false);
        }
        /// <summary>
        /// 获取集合中的所有元素
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public static async Task<List<T>> SMembersAsync<T>(string key)
        {
            return (await _redisClient.SMembersAsync<T>(key).ConfigureAwait(false)).ToList();
        }
        /// <summary>
        /// 从集合中移除元素
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        public static async Task<long> SRemAsync<T>(string key, T data)
        {
            return await _redisClient.SRemAsync(key, data).ConfigureAwait(false);
        }
        /// <summary>
        /// 从集合中移除元素
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="datas"></param>
        /// <returns></returns>
        public static async Task<long> SRemListAsync<T>(string key, List<T> datas)
        {
            return await _redisClient.SRemAsync(key, datas).ConfigureAwait(false);
        }
        /// <summary>
        /// 是否存在
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static bool Exists(string key)
        {
            return _redisClient.Exists(key);
        }
        /// <summary>
        /// 是否存在
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static async Task<bool> ExistsAsync(string key)
        {
            return await _redisClient.ExistsAsync(key).ConfigureAwait(false);
        }
        /// <summary>
        /// 获取分布式锁
        /// 在指定时间内会一直尝试获取锁
        /// 直到获取锁或者超时
        /// </summary>
        /// <param name="key"></param>
        /// <param name="expiry"></param>
        /// <returns>锁的持有者，通过这个持有者进行解锁。  如果返回 null 表示没有获取到锁</returns>
        public static CSRedisClientLock LockTask(string key, TimeSpan expiry)
        {
            return _redisClient.Lock(key, expiry.Seconds);
        }
        /// <summary>
        /// 获取分布式锁
        /// 如果没有获取到锁则返回false
        /// </summary>
        /// <param name="key"></param>
        /// <param name="expiry"></param>
        /// <returns>锁的持有者，通过这个持有者进行解锁。  如果返回 null 表示没有获取到锁</returns>
        public static CSRedisClientLock TryLockTask(string key, TimeSpan expiry)
        {
            return _redisClient.TryLock(key, expiry.Seconds);
        }

        /// <summary>
        /// 累加器
        /// incrNumber 为正数时为累加，为负数时为递减
        /// </summary>
        /// <param name="key"></param>
        /// <param name="incrNumber"></param>
        /// <returns>累加之后的值</returns>
        public static long GetIncrement(string key, int incrNumber = 1)
        {
            return _redisClient.IncrBy(key, incrNumber);
        }

        /// <summary>
        /// 限流器(滑动时间窗口)
        /// </summary>
        /// <param name="key">要限流的标识</param>
        /// <param name="limit">最大限流数量</param>
        /// <param name="expiry">有效期</param>
        /// <returns>true: 表示没有达到最大限流数量，false:表示超过了最大限流数量</returns>
        public static bool TryAcquire(string key, int limit, TimeSpan expiry)
        {
            string luaScript = @"local key = KEYS[1] 
                                 local limit = tonumber(ARGV[1]) 
                                 local current = tonumber(redis.call('get', key) or '0') 
                                 if current + 1 > limit then return 0 
                                 else redis.call('INCRBY', key, 1) redis.call('EXPIRE', key, ARGV[2]) return 1 end";
            var result = _redisClient.Eval(luaScript, key, limit, expiry.TotalSeconds).ToString();
            return result == "1";
        }
        /// <summary>
        /// 限流器(固定时间窗口)
        /// </summary>
        /// <param name="key"></param>
        /// <param name="limit"></param>
        /// <param name="window"></param>
        /// <returns></returns>
        public static bool TryAcquireFixedWindow(string key, int limit, TimeSpan window)
        {
            string luaScript = @"local key = KEYS[1] 
                                 local limit = tonumber(ARGV[1]) 
                                 local current = tonumber(redis.call('get', key) or '0') 
                                 if current + 1 > limit then return 0 
                                 else if current == 0 then redis.call('SET', key, 1, 'PX', ARGV[2]) 
                                 else redis.call('INCRBY', key, 1) end
                                 return 1 end";
            var result = _redisClient.Eval(luaScript, key, limit, window.TotalMilliseconds).ToString();
            return result == "1";
        }
    }
}
